require "file_analyser"
require 'help'
require 'fragmenter'
require 'flickr_uploader'
require 'grin_exec'
require 'grin_generator'
require 'freehand'
require 'backup_storage'


class StoryTeller

  attr_reader :all_help
 
  #``````````````````
  # constructor
  #``````````````````

  def initialize(storyteller_dir = ".")
    @all_help = []
    @home = storyteller_dir
    @config = FileAnalyser.new
    @properties_file = "#{@home}/.properties"
    @config.load_config(@properties_file) if valid?
    @local_analyser = FileAnalyser.new
  end

  #``````````````````
  # interface
  #``````````````````
  
  def StoryTeller.extractFileData(input)
    image_data = input.split('"')
    if image_data.length == 1
      image_data = input.split
    end

    image_url = image_data[1] || ""
    label_text = (image_data[2..-1].join " ").strip if image_data.length >= 2
    [image_url,label_text || ""]
  end
  
  def parse(argv)
    return if argv.nil?
    @all_help = [] 
    command = argv.shift
    if command != "new" and !valid?
      @all_help << Help.no_repository
      @all_help << Help.repository_usage
    end
    
    case command
      when "new"
        storyteller_dir = argv.shift || "storyteller"
        initialize storyteller_dir
        assign_home
        @all_help << Help.repository_created(@home) if valid?

      when "add"
        if argv.length == 0
          @all_help << Help.no_directory
          @all_help << Help.add_usage
          return
        end

        if argv[1].nil?
          name = ""
          root = argv[0]
        else
          name = argv[0]
          root = argv[1]
        end

        add_media(name, root)

      when "update"
        media_directories.each {|directory| update_media("",directory) }

      when "ignore"
        argv.each { |blah| ignore(blah) }
        
      when "tag"
        local_dir = argv.shift
        text = argv.shift
        tag local_dir, text
      
      when "flickr"
        @all_help << Help.flickr_usage and return if argv.length == 0
        upload_flickr argv

      when "flash"
        @all_help << Help.flash_usage and return if argv.length < 2
        swf_file = argv.shift  
        flash swf_file, argv
      
      when "compile"
        @all_help << Help.compile_usage and return if argv.length == 0
        grin_file = argv.shift
        
        if argv.length == 0
          swf_file = FileAnalyser.rename_file_extension(grin_file,".swf")
          smil_file = FileAnalyser.rename_file_extension(grin_file,".smil")
          compile grin_file, swf_file, "flash"
          compile grin_file, smil_file, "smil"
        else
          output_file = argv.shift
          file_type = argv.shift
          
          if (file_type == "flash" or file_type == "smil")
            compile grin_file, output_file, file_type
          else
            
            as_swf_file = FileAnalyser.rename_file_extension(output_file,".swf")
            as_smil_file = FileAnalyser.rename_file_extension(output_file,".smil")
            
            if (as_swf_file == output_file)
              compile grin_file,  "#{output_file}", "flash"            
            elsif (as_smil_file == output_file)
              compile grin_file, "#{output_file}", "smil"
            else
              compile grin_file,  "#{output_file}.swf", "flash"            
              compile grin_file, "#{output_file}.smil", "smil"
            end
          end
        end

      when "freehand"
        freehand = Freehand.new(self, argv[0])
        freehand.run
      
      when "backup"
        storage = BackupStorage.new(self,argv[0])
        storage.run
    end
  end
  
  def assign_home
    if valid?
      @config.load_config(@properties_file)
    else
      FileUtils.mkdir(@home) unless File.exist? @home
      File.open(@properties_file,"a") { |file| file.puts "# StoryTeller configuration"  }
      @config.load_config(@properties_file)
      
      @config.add_property "flickr_api_key", 'f664bcb86468af50ff33c228b385eb59'
      @config.add_property "flickr_shared_secret", 'dc74f2f7693d245a'
    end
  end
  
  def valid?
    File.exist? @properties_file
  end
    
  def add_media(name, directory)
  
    unless File.exists? directory
      @all_help << Help::unknown_directory_for(directory) unless File.exists?(directory)
      @all_help << Help.add_usage and return if @all_help.length > 0
      return
    end
  
    backup = analyse_directory(name, directory)
    number_of_files = store backup

    @all_help << Help::backup(backup,number_of_files)
    @config.add_property "media_directory", backup.root
    backup
  end
  
  def update_media(name, directory)
    backup = analyse_directory(name, directory)
    number_of_files_added, number_of_files_removed = store backup
    @all_help << Help::backup(backup, number_of_files_added, number_of_files_removed)
    backup
  end

  def tag(local_dir,text)
    tag_analyser = FileAnalyser.new
    tag_analyser.load_config(qualified_file_for(local_dir))
    answer = tag_analyser.add_property "tag", text
  end

  def property_of(local_dir,key)
    FileAnalyser.property_of(local_dir,key)
  end  
  
  def properties_of(local_dir,key)
    FileAnalyser.properties_of(local_dir,key)
  end    
  
  def path_of(local_dir)
    FileAnalyser.path_of local_dir
  end  
  
  def ignore(pattern)
    @config.add_property "ignore", pattern
    @all_help << Help.ignore(pattern)
  end

  def ignoring
    should_ignore = @config.properties_of "ignore"
    should_ignore << ".dir"
    
    should_ignore
  end

  def media_directories
    @config.properties_of "media_directory"
  end

  def add_file_to(generator, file, label = nil)
    label = property_of(file, "tag") if label.nil? or label.length == 0
    if FileAnalyser.image? file
      generator.add_image(path_of(file),label)
    else 
      generator.add_text label unless label.nil?
    end
  end
  
  def size
    size_to_date = 0
    @config.ignoring << ".dir"
    @config.ignoring << ".properties"
    @config.files_in(@home).each do |file|
      true_file = path_of(file)
      size_to_date += @config.size_of(true_file)
    end
    size_to_date
  end
  
  #``````````````````
  # private methods
  #``````````````````
    
  private
    
    def internal_file?(path)
      path =~ /dir$/ or path =~ /properties$/
    end
    
    def directory_of(path)
      length = path.length - File.basename(path).length - 1
      "#{path[0..length]}"
    end
    
    def destination_file_for(path,where)
      start_index = @home.length
      "#{where}#{path[start_index..-1]}"
    end
    
    def qualified_file_for(local_dir)
      qualified_dir = "#{@home}/#{local_dir}"
      qualified_dir = "#{qualified_dir}/.dir" if File.directory? qualified_dir
      qualified_dir
    end

    def analyse_directory(name, directory)
      frag = Fragmenter.new
      frag.ignoring = ignoring
      backup = frag.add name, directory
      backup
    end
      
    def store(backup)
  
      directory = @home
      if backup.has_name?
        directory = "#{directory}/#{backup.name}"
        FileUtils.mkdir("#{directory}")
      end

      is_existing = File.exists?("#{directory}/.dir")
      added_to_date = store_directory directory, backup.root, backup.file_delimiter, false
      removed_to_date = is_existing ? 0 : nil
      
      @local_analyser.ignoring = ignoring
      backup.directories.each do |backup_dir|
        new_directory = "#{directory}#{backup_dir}"
        qualified_dir = backup.qualified_directory_of backup_dir
        added_to_date += store_directory new_directory, qualified_dir, backup.file_delimiter
      end
      [added_to_date,removed_to_date]
    end
    
    def store_directory(new_directory, data_dir, file_delimiter, is_recursive = true)
      added_to_date = 0
      should_keep = false

      FileUtils.mkdir_p(new_directory) unless File.exists? new_directory
      create_relative_file "#{new_directory}/.dir", "#{data_dir}" unless File.exists? "#{new_directory}/.dir"
      
      @local_analyser.files_in(data_dir, is_recursive).each do |filename|
        new_filename = "#{new_directory}#{file_delimiter}#{File.basename(filename)}"
        unless File.exists? new_filename
          added_to_date += 1
          create_relative_file "#{new_filename}", "#{filename}"
        end
      end
      added_to_date
    end
    
    
  
    def create_relative_file(relative_file,absolute_file)
      File.open("#{relative_file}","a") { |file| file.puts "path=#{absolute_file}" }
    end
    
    def upload_flickr all_relative_files
      begin
        @config.ignoring = ignoring
        
        all_files = []
        all_relative_files.each do |relative_file|
          if File.directory? relative_file
            @config.files_in(relative_file).each { |internal_file| all_files << path_of(internal_file) }
          else
            all_files << path_of(relative_file)
          end
        end
        
        api_key = @config.property_of "flickr_api_key"
        shared_secret = @config.property_of "flickr_shared_secret"
        token = @config.property_of "flickr_token"
      
        uploader = FlickrUploader.new(api_key,shared_secret)
        if (token.nil?)
          token = uploader.request_new_token
          @config.add_property "flickr_token", token
        else
          uploader.load_token token        
        end
        
        uploader.login
        number_of_files_uploaded = uploader.upload_pictures all_files
        @all_help << Help.flickr_uploaded(number_of_files_uploaded)
      rescue FlickRaw::FailedResponse => e
        @all_help << Help.flickr_upload_failed(e)
        return
      end
    end
    
    def flash swf_file, all_files
      grin_file = FileAnalyser.rename_file_extension(swf_file,".grin")
      generator = GrinGenerator.new(grin_file)
      can_generate = false
      return unless add_to_flash generator, all_files
      generator.save
      compile grin_file, swf_file, "flash"
    end
    
    def compile in_file, out_file, file_type
      @all_help << Help.unknown_file(in_file) and return unless File.exists? in_file
      did_compile = GrinExec.run in_file, out_file, file_type
      @all_help << Help.compile_complete(in_file, out_file) if did_compile
    end

    def add_to_flash(generator, all_files)
      can_generate = false
      all_files.each do |file|
        @all_help << Help.unknown_file(file) and next unless File.exists? file
        can_generate = true
        add_file_to generator, file        
        @local_analyser.ignoring = ignoring
        add_to_flash generator, @local_analyser.files_in(file) if File.directory? file 
      end
      can_generate
    end
end
